Jetpack Composeで動的なスタンプカードを実装する
本記事のiOS版は「SwiftUIで動的なスタンプカードを実装する」をご覧ください。
パンに付いてくるシールを集めて白いお皿と交換するなど日常的な商取引でもスタンプカードを利用していることは多い。
コンシューマ向けアプリを開発していると、それと同様に来店回数やランキングによってスタンプを付与し、一定数のスタンプと商品とを交換する機能を実装することがある。たとえば、フィットネスアプリの場合、ユーザーが定められた運動目標を達成するごとにスタンプを付与し、一定数を集めるとヘルスケア製品の割引などの報酬を得られる。
この記事では、LazyVerticalGrid
を使用して簡単なスタンプカードを実装する方法を紹介する。
スタンプカードの実装
スタンプカードのようにスタンプを等間隔で並べて表示するにはLazyVerticalGrid
が適している。
以下はスタンプをグリッドレイアウトで表示する画面の実装例である。現在16個中2個のスタンプを獲得した状態を表現している。
スタンプのViewを実装する
各スタンプは色でアクティブか非アクティブかを区別する。まず、アクティブなスタンプViewを実装する。指定された色で塗りつぶされた円をユーザーが獲得したスタンプとする。
@Composable fun ActiveStampView(text: String, stampColor: Color) { Box( modifier = Modifier .size(50.dp) .clip(CircleShape) .background(stampColor) ) { Text( text = text, color = Color.White, modifier = Modifier.align( Alignment.Center ) ) } }
次に、非アクティブなスタンプViewを実装する。このViewはスタンプ未獲得状態を破線の円で示す。
@Composable fun InactiveStampView(text: String, stampColor: Color) { Box( modifier = Modifier .size(50.dp) .background(Color.Transparent, CircleShape) ) { Canvas(modifier = Modifier.matchParentSize()) { val center = this.center val radius = size.minDimension / 2 drawCircle( color = stampColor, radius = radius, center = center, style = Stroke( width = 2.dp.toPx(), pathEffect = PathEffect.dashPathEffect(floatArrayOf(35f, 10f)) ) ) } Text( text = text, color = stampColor, modifier = Modifier.align( Alignment.Center ) ) } }
スタンプ画面の実装
16個中2個のスタンプを獲得のような状態を表現するために、num: 2
とrequired: 16
をパラメータとする StampScreen
を実装した。
@Composable fun StampScreen( num: Int, required: Int, activeStampColor: Color = Color.Red, inactiveStampColor: Color = Color.Gray ) { LazyVerticalGrid( columns = GridCells.Fixed(7), contentPadding = PaddingValues(all = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { item(span = { GridItemSpan(maxCurrentLineSpan) }) { Column { Text( text = "$num / $required", fontSize = 20.sp, fontWeight = FontWeight.Bold, color = Color.Black, textAlign = TextAlign.Center, modifier = Modifier.align(Alignment.CenterHorizontally) ) Spacer(modifier = Modifier.height(16.dp)) } } items((0 until required).toList()) { index -> if (index < num) { ActiveStampView( text = "${index + 1}", stampColor = activeStampColor ) } else { InactiveStampView( text = "${index + 1}", stampColor = inactiveStampColor ) } } } }
上記の StampScreen
を表示させるプレビューを実装する。
@Preview(showBackground = true) @Composable fun StampScreenPreview() { SampleStamp3Theme { StampScreen(2, 16) } }
プレビューウィンドウにスタンプカード画面が表示された。
ActiveStampView
や InactiveStampView
の実装を変更することで、ハート型や星形など様々な形状のViewへの対応や、アプリバイナリにスタンプ画像をリソースとして組み込んでおき利用したり、サーバーからの画像をダウンロードして表示させたりすることも実現できるだろう。
まとめ
Jetpack Composeを使用することで、LazyVerticalGrid
や LazyHorizontalGrid
を活用して、簡単にグリッドレイアウトのViewを表示することが可能である。スタンプカードのようなにスタンプを規則正しく並べて表示するなどの用途に向いている。またサンプルコードで示したようにitem
やitems
を組み込むことで、1列のアイテムと7列のアイテムを混在させて並べられる。おそらくRecyclerView
とGridLayoutManager
を使った実装よりも直感的に扱うことができるだろう。